package com.example.mq.mqserver.core;

import com.example.mq.common.MqException;

/*
 * 用来实现交换机的转发规则
 * 验证bindingkey是否合法
 * 数字字母下划线 .分割*/
public class Router {
    public boolean checkBindingKey(String bindingKey) {
        if(bindingKey.length()==0)return true;//直接交换机bindingKey=""
        for(int i=0;i<bindingKey.length();i++){
            char ch=bindingKey.charAt(i);
            if(ch>='A'&&ch<='Z')continue;
            else if(ch>='a'&&ch<='z')continue;
            else if(ch>='0'&&ch<='9')continue;
            else if(ch=='.'||ch=='#'||ch=='_'||ch=='*')continue;
            else return false;
        }

        //aa.a*.fd这种是不合法的 * #被认为是一个段落
        String[] s=bindingKey.split("\\.");//要转\.表示. （正则表达式）在java中\是一个特殊字符，需要用\转意，所以最终是\\.
        for(int i=0;i<s.length;i++){
            if((s[i].contains("#")||s[i].contains("*"))&&s[i].length()>1)return false;
        }

        //aa.#.#非法（前三种繁琐，功能增加少）
        //aa.#.*.bbb非法
        //aa.*.#.ccc非法
        //aa.*.*.cc合法
        for(int i=0;i<s.length-1;i++){
            if(s[i].equals("#")){
                if(s[i+1].equals("#")||s[i+1].equals("*"))return false;
            }else if(s[i].equals("*")){
                if(s[i+1].equals("#"))return false;
            }
        }

        return true;
    }

    //routingkey只能是数字字母下划线使用.分割
    public boolean checkRoutingKey(String routingKey) {

        if (routingKey.length() == 0) {
            return true;//fanout交换机中不关注routingke，所以是合法的
        }

        for (int i = 0; i < routingKey.length(); i++) {
            if (routingKey.charAt(i) == '.' || routingKey.charAt(i) == '_') {
                continue;
            } else if (routingKey.charAt(i) >= 'a' && routingKey.charAt(i) <= 'z') {
                continue;
            } else if (routingKey.charAt(i) >= 'A' && routingKey.charAt(i) <= 'Z') {
                continue;
            } else if (routingKey.charAt(i) >= '0' && routingKey.charAt(i) <= '9') {
                continue;
            } else return false;
        }
        return true;
    }

    public boolean route(ExchangeType exchangeType, Binding binding, Message message) throws MqException {
        if (exchangeType == ExchangeType.FANOUT) {
            return true;//无脑返回true
        } else if (exchangeType == ExchangeType.TOPIC) {
            return routeTopic(binding, message);
        } else {
            throw new MqException("[Router] 交换机类型非法");
        }
    }

    private boolean routeTopic(Binding binding, Message message) {
        String[] bindingKey = binding.getBindingKey().split("\\.");
        String[] routingKey = message.getRoutingKey().split("\\.");
        //如果是普通字符，完全相等 两个指针一起移动
        //如果* 两个指针一起走
        // 如果# binding后面没有了，直接return true
        //如果# 后面有字符，移动到字符位置
        //如果出循环后，有一个指针没有满，return false
        int bindIndex=0;
        int routingIndex=0;
        while(bindIndex<bindingKey.length&&routingIndex<routingKey.length){
            if(!bindingKey[bindIndex].equals("*")&&!bindingKey[bindIndex].equals("#")){//普通字符
                if(!bindingKey[bindIndex].equals(routingKey[routingIndex])){
                    return false;
                }
                bindIndex++;
                routingIndex++;
            }else if(bindingKey[bindIndex].equals("*")){//*通配符
                bindIndex++;
                routingIndex++;
            }else if(bindingKey[bindIndex].equals("#")&&bindIndex+1>=bindingKey.length){//#通配符，bindingKey后面没有别的条件了
                return true;
            }else if(bindingKey[bindIndex].equals("#")&&!(bindIndex+1>=bindingKey.length)){
                String word=bindingKey[bindIndex+1];
                bindIndex+=2;//等一下默认bindIndex+1和word匹配完成，所以还要+1
                for(int i=routingIndex;i<routingKey.length;i++){
                    if(word.equals(routingKey[i])){
                        routingIndex=i;
                        break;
                    }
                }
                if(word.equals(routingKey[routingIndex])){
                    routingIndex++;
                }else{
                    return false;
                }
            }
        }

        if(routingIndex>=routingKey.length&&bindIndex>=bindingKey.length){
            return true;
        }

        return false;
    }
}
